#数据访问层

from bson import ObjectId
import pymongo
from pymongo.collection import Collection
from datetime import datetime, timezone
import hashlib
import utils

class ReturnObjct(object):
    ##返回的对象
    def __init__(self):
        self.code = 0
        self.msg = 'ok'
    def to_json(self):
        return {"code":self.code, "msg":self.msg}

class Session(object):
    #会话对象
    COLL_NAME = "sessions"
    INDEX_SESSION = "session_index"
    FIELD_TOKEN = "token"
    FIELD_OBJ_ID = "obj_id" #对象id
    FIELD_OBJ_TYPE = "obj_type" #对象类型
    FIELD_LAST_ACTIVE = "last_active"
    
    TIME_OUT_SECONDS = 300
    
    OT_DEVICE = 1
    OT_USER = 2
    
    def __init__(self):
        self._id = None
        self.obj_id = None #对象id，device或user的id
        self.token = None #对象令牌
        self.obj_type = None
        #最后活动时间，因为Mongodb用的是UTC时间，所以这个地方要指定utc时区
        self.last_active = None
    
    def update(self):
        self.last_active = datetime.utcnow()
        
    def new_token(self):
        self.token = str(ObjectId())
        
    def to_json(self):
        return {FIELD_ID: self._id,
                Session.FIELD_TOKEN:self.token,
                Session.FIELD_OBJ_ID: self.obj_id,
                Session.FIELD_OBJ_TYPE: self.obj_type,
                Session.FIELD_LAST_ACTIVE: self.last_active}
    def from_json(self, data: dict):
        if data.get(FIELD_ID):
            self._id = str(data.get(FIELD_ID))
        self.token = data.get(Session.FIELD_TOKEN)
        self.obj_id = data.get(Session.FIELD_OBJ_ID)
        self.obj_type = data.get(Session.FIELD_OBJ_TYPE)
        self.last_active = data.get(Session.FIELD_LAST_ACTIVE)
  
class Device(object):
    #设备
    COLL_NAME = "devices"
    FIELD_KEY = "key"
    FIELD_NAME = "name"
    
    def __init__(self):
        self._id = None
        self.key = None
        self.name = None

class User(object):
    #用户
    COLL_NAME = "users"
    FIELD_NAME = "name"
    FIELD_PWD = "pwd"
    
    def __init__(self):
        self._id = None
        self.name = None
        self.pwd = None

class DeviceData(object):
    #设备数据
    COLL_NAME = "device_data"
    FIELD_TEMPERATURE = "temperature"
    FIELD_PRESSURE = "pressure"
    FIELD_VOLTAGE = "voltage"
    FIELD_POSITION = "position"
    FIELD_SWITCH = "switch"
    FIELD_ALTITUDE = "altitude"
    FIELD_TIME = "time"
    
    def __init__(self):
        self._id = None
        self.temperature = None
        self.pressure = None
        self.altitude = None
        self.voltage = None
        self.position = None
        self.switch = None
        self.time = None

    def to_json(self):
        return {FIELD_ID: self._id,
                DeviceData.FIELD_TEMPERATURE:self.temperature,
                DeviceData.FIELD_PRESSURE: self.pressure,
                DeviceData.FIELD_ALTITUDE: self.altitude,
                DeviceData.FIELD_VOLTAGE: self.voltage,
                DeviceData.FIELD_POSITION: self.position,
                DeviceData.FIELD_SWITCH: self.switch,
                DeviceData.FIELD_TIME: self.time}
    
    def from_json(self, data: dict):
        if data.get(FIELD_ID):
            self._id = str(data.get(FIELD_ID))
        self.temperature = data.get(DeviceData.FIELD_TEMPERATURE)
        self.pressure = data.get(DeviceData.FIELD_PRESSURE)
        self.altitude = data.get(DeviceData.FIELD_ALTITUDE)
        self.voltage = data.get(DeviceData.FIELD_VOLTAGE)
        self.position = data.get(DeviceData.FIELD_POSITION)
        self.switch = data.get(DeviceData.FIELD_SWITCH)
        self.time = data.get(DeviceData.FIELD_TIME)

class LEDState(object):
    #led灯状态对象
    COLL_NAME = "led"
    FIELD_STATE = "state"
    FIELD_TIME = "time"
    def __init__(self):
        self._id: str = None
        self.state:bool = None
        self.time: str = None
    
    def to_json(self):
        return {FIELD_ID: self._id,
                LEDState.FIELD_STATE:self.state,
                LEDState.FIELD_TIME: self.time}
    def from_json(self, data):
        if data.get(FIELD_ID):
            self._id = str(data.get(FIELD_ID))
        self.state = data.get(LEDState.FIELD_STATE)
        self.time = data.get(LEDState.FIELD_TIME)

DB_HOST = "localhost"
DB_PORT = 27017
DB_NAME = "iot"
FIELD_ID = "_id"

def db_collection(coll_name)->Collection:
    #获取一个集合
    client:pymongo.MongoClient = pymongo.MongoClient(host=DB_HOST, port=DB_PORT)
    return client[DB_NAME][coll_name]


def insert_one(coll: Collection, data: dict)->str:
    #插入记录\
    _id = data.get(FIELD_ID)
    if _id==None:
        _id = ObjectId() ##新生成一个ObjectId
        data[FIELD_ID] = _id
    else:
        if isinstance(_id, str):
            _id = ObjectId(_id) ##把字符串转换成ObjectId
            data[FIELD_ID] = _id
    coll.insert_one(data)
    return str(_id)

def update_one(coll: Collection, filter, data: dict):
    coll.update_one(filter=filter,update={"$set": data})
    
def find_one_by_id(coll: Collection, _id: str):
    return coll.find_one({FIELD_ID: ObjectId(_id)})

def update_one_by_id(coll: Collection, _id, data):
    if isinstance(_id, str):
        _id = ObjectId(_id) ##把字符串转换成ObjectId
    coll.update_one(filter={FIELD_ID:_id},update={"$set": data})

def check_session_index(flag={}):
    if flag.get("index"):
        return #索引已经存在，直接返回
    #判断索引是否存在
    coll = db_collection(Session.COLL_NAME)
    info = coll.index_information()
    if info.get(Session.INDEX_SESSION):
        #如果索引存在，设置检查标志，然后返回
        flag["index"] = 1
        return
    #如果索引不存在，创建一个过期自动删除记录的索引
    coll.create_index([(Session.FIELD_LAST_ACTIVE,1)], name=Session.INDEX_SESSION, expireAfterSeconds=Session.TIME_OUT_SECONDS)
    
               
def create_session(obj_id: str, obj_type: int)->Session:
    #创建会话对象
    check_session_index()
    coll = db_collection(Session.COLL_NAME)
    #删除已有的会话，一次只允许一个地方登录，后登陆的会把前面的顶掉
    coll.delete_one({Session.FIELD_OBJ_ID: obj_id, Session.FIELD_OBJ_TYPE: obj_type})
    #新建一个会话
    session = Session()
    session.obj_id = obj_id
    session.obj_type = obj_type
    session.new_token()
    session.update()
    insert_one(coll, session.to_json())
    return session


def get_session(token: str, obj_type: int)->Session:
    #获取会话对象
    coll = db_collection(Session.COLL_NAME)
    data = coll.find_one({Session.FIELD_TOKEN: token, Session.FIELD_OBJ_TYPE: obj_type})
    if data:
        session = Session()
        session.from_json(data)
        session.update() #更新最后的活动时间
        #获取会话的同时更新时间戳，用upsert避免在更新时刚好删掉
        coll.update_one({FIELD_ID: data.get(FIELD_ID)},
                        {"$set":
                         {FIELD_ID: data.get(FIELD_ID),
                          Session.FIELD_TOKEN: token,
                          Session.FIELD_OBJ_ID: session.obj_id,
                          Session.FIELD_OBJ_TYPE: obj_type,
                          Session.FIELD_LAST_ACTIVE: session.last_active
                          }}, upsert=True)
        return session
    return None


def get_user_id(name: str, pwd: str)->str:
    #用户登录，如果用户名密码正确，返回用户的ID，用于会话
    if pwd==None:
        pwd = ""
    md5_pwd = utils.md5_string(pwd)
    coll = db_collection(User.COLL_NAME)
    rec = coll.find_one({User.FIELD_NAME:name, User.FIELD_PWD: md5_pwd})
    if rec:
        return str(rec.get(FIELD_ID))
    return None

def get_device_id(key: str):
    #设备登录，返回设备ID，用于会话
    if key==None:
        return None
    coll = db_collection(Device.COLL_NAME)
    rec = coll.find_one({Device.FIELD_KEY:key})
    if rec:
        return str(rec.get(FIELD_ID))
    return None #设备key不存在返回None

def insert_device_data(device_data: DeviceData):
    #向device_data集合插入数据
    coll = db_collection(DeviceData.COLL_NAME)
    return insert_one(coll, device_data.to_json())



def get_device_data(date_from, date_to):
    ##根据日期获取设备数据
    if date_from:
        filter1 = {DeviceData.FIELD_TIME:{'$gte':date_from}}
    else:
        filter1 = None
    if date_to:
        filter2 = {DeviceData.FIELD_TIME:{'$lte':date_to}}
    else:
        filter2 = None
    if filter1 and filter2:
        filter = {'$and':[filter1,filter2]}
    else:
        if filter1:
            filter = filter1
        else:
            if filter2:
                filter = filter2
            else:
                filter = {}
    coll = db_collection(DeviceData.COLL_NAME)
    #查询最近20条的记录
    cursor = coll.find(filter).sort([(FIELD_ID, pymongo.DESCENDING)]).limit(20)
    dds = []
    for rec in cursor:
        dd = DeviceData()
        dd.from_json(rec)
        dds.append(dd)
    return dds

def insert_led_state(led_state: LEDState):
    #插入led状态数据
    coll = db_collection(LEDState.COLL_NAME)
    return insert_one(coll, led_state.to_json())


def get_last_led_state():
    #获取最后的led状态
    coll = db_collection(LEDState.COLL_NAME)
    data = coll.find_one(sort=[(LEDState.FIELD_TIME, pymongo.DESCENDING)])
    if data:
        state = LEDState()
        state.from_json(data)
        return state
    return None

def test01():
    #测试代码
    d = DeviceData()
    d.pressure = 10
    d.position = 10
    d.temperature = 37.5
    d.switch = True
    d.voltage = 1.5
    d.time = datetime.now()
    coll = db_collection(DeviceData.COLL_NAME)
    _id:str = insert_one(coll, d)
    print(_id)
    
def test02():
    #测试代码
    obj_id = str(ObjectId())
    session = create_session(obj_id)
    print(session.token, session.last_active)
    import time
    for i in range(20):
        time.sleep(3)
        session = get_session(session.token)
        print(session.token, session.last_active)

def test_get_device_data():
    #测试代码
    dds = get_device_data(None, None)
    for dd in dds:
        print(dd.to_json())
        
if __name__ == "__main__":
    #test02()
    test_get_device_data()

    
